{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Note-Saving Tool with Pydantic and Anthropic Tool Use\n",
    "\n",
    "In this example, we'll create a tool that saves a note with the author and metadata, and use Pydantic to validate the model's response when calling the tool. We'll define the necessary Pydantic models, process the tool call, and ensure that the model's response conforms to the expected schema."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 1: Set up the environment\n",
    "First, let's install the required libraries and set up the Claude API client."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%pip install anthropic pydantic 'pydantic[email]'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "from anthropic import Anthropic\n",
    "from pydantic import BaseModel, EmailStr, Field\n",
    "\n",
    "client = Anthropic()\n",
    "MODEL_NAME = \"claude-opus-4-1\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 2: Define the Pydantic models\n",
    "\n",
    "We'll define Pydantic models to represent the expected schema for the note, author, and the model's response. This will allow us to validate and type-check the model's response when saving a note."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Author(BaseModel):\n",
    "    name: str\n",
    "    email: EmailStr\n",
    "\n",
    "\n",
    "class Note(BaseModel):\n",
    "    note: str\n",
    "    author: Author\n",
    "    tags: list[str] | None = None\n",
    "    priority: int = Field(ge=1, le=5, default=3)\n",
    "    is_public: bool = False\n",
    "\n",
    "\n",
    "class SaveNoteResponse(BaseModel):\n",
    "    success: bool\n",
    "    message: str"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 3: Define the client-side tool\n",
    "\n",
    "Next, we'll define the client-side tool that our chatbot will use to save notes."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "tools = [\n",
    "    {\n",
    "        \"name\": \"save_note\",\n",
    "        \"description\": \"A tool that saves a note with the author and metadata.\",\n",
    "        \"input_schema\": {\n",
    "            \"type\": \"object\",\n",
    "            \"properties\": {\n",
    "                \"note\": {\"type\": \"string\", \"description\": \"The content of the note to be saved.\"},\n",
    "                \"author\": {\n",
    "                    \"type\": \"object\",\n",
    "                    \"properties\": {\n",
    "                        \"name\": {\"type\": \"string\", \"description\": \"The name of the author.\"},\n",
    "                        \"email\": {\n",
    "                            \"type\": \"string\",\n",
    "                            \"format\": \"email\",\n",
    "                            \"description\": \"The email address of the author.\",\n",
    "                        },\n",
    "                    },\n",
    "                    \"required\": [\"name\", \"email\"],\n",
    "                },\n",
    "                \"priority\": {\n",
    "                    \"type\": \"integer\",\n",
    "                    \"minimum\": 1,\n",
    "                    \"maximum\": 5,\n",
    "                    \"default\": 3,\n",
    "                    \"description\": \"The priority level of the note (1-5).\",\n",
    "                },\n",
    "                \"is_public\": {\n",
    "                    \"type\": \"boolean\",\n",
    "                    \"default\": False,\n",
    "                    \"description\": \"Indicates whether the note is publicly accessible.\",\n",
    "                },\n",
    "            },\n",
    "            \"required\": [\"note\", \"author\"],\n",
    "        },\n",
    "    }\n",
    "]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 4: Implement the note-saving tool\n",
    "We'll create a dummy note saving function that just prints out that the note was saved successfully. If you actually want this note to be saved somewhere, you can implement this function."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "def save_note(note: str, author: dict, priority: int = 3, is_public: bool = False) -> None:\n",
    "    print(\"Note saved successfully!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 5: Process the tool call and generate the response\n",
    "We'll create functions to process the tool call made by Claude and generate the response indicating the success of saving the note."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "def process_tool_call(tool_name, tool_input):\n",
    "    if tool_name == \"save_note\":\n",
    "        note = Note(\n",
    "            note=tool_input[\"note\"],\n",
    "            author=Author(name=tool_input[\"author\"][\"name\"], email=tool_input[\"author\"][\"email\"]),\n",
    "            priority=tool_input.get(\"priority\", 3),\n",
    "            is_public=tool_input.get(\"is_public\", False),\n",
    "        )\n",
    "        save_note(note.note, note.author.model_dump(), note.priority, note.is_public)\n",
    "        return SaveNoteResponse(success=True, message=\"Note saved successfully!\")\n",
    "\n",
    "\n",
    "def generate_response(save_note_response):\n",
    "    return f\"Response: {save_note_response.message}\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 6: Interact with the chatbot\n",
    "\n",
    "Now, let's create a function to interact with the chatbot. We'll send a user message, process the tool call made by Claude, generate the response, validate the model's response using Pydantic, and return the final response to the user."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "def chatbot_interaction(user_message):\n",
    "    print(f\"\\n{'=' * 50}\\nUser Message: {user_message}\\n{'=' * 50}\")\n",
    "\n",
    "    messages = [{\"role\": \"user\", \"content\": user_message}]\n",
    "\n",
    "    message = client.messages.create(\n",
    "        model=MODEL_NAME, max_tokens=4096, tools=tools, messages=messages\n",
    "    )\n",
    "\n",
    "    print(\"\\nInitial Response:\")\n",
    "    print(f\"Stop Reason: {message.stop_reason}\")\n",
    "    print(f\"Content: {message.content}\")\n",
    "\n",
    "    if message.stop_reason == \"tool_use\":\n",
    "        tool_use = next(block for block in message.content if block.type == \"tool_use\")\n",
    "        tool_name = tool_use.name\n",
    "        tool_input = tool_use.input\n",
    "\n",
    "        print(f\"\\nTool Used: {tool_name}\")\n",
    "        print(f\"Tool Input: {tool_input}\")\n",
    "\n",
    "        save_note_response = process_tool_call(tool_name, tool_input)\n",
    "\n",
    "        print(f\"Tool Result: {save_note_response}\")\n",
    "\n",
    "        response = client.messages.create(\n",
    "            model=MODEL_NAME,\n",
    "            max_tokens=4096,\n",
    "            messages=[\n",
    "                {\"role\": \"user\", \"content\": user_message},\n",
    "                {\"role\": \"assistant\", \"content\": message.content},\n",
    "                {\n",
    "                    \"role\": \"user\",\n",
    "                    \"content\": [\n",
    "                        {\n",
    "                            \"type\": \"tool_result\",\n",
    "                            \"tool_use_id\": tool_use.id,\n",
    "                            \"content\": str(save_note_response),\n",
    "                        }\n",
    "                    ],\n",
    "                },\n",
    "            ],\n",
    "            tools=tools,\n",
    "        )\n",
    "    else:\n",
    "        response = message\n",
    "\n",
    "    final_response = next(\n",
    "        (block.text for block in response.content if hasattr(block, \"text\")),\n",
    "        None,\n",
    "    )\n",
    "    print(response.content)\n",
    "    print(f\"\\nFinal Response: {final_response}\")\n",
    "\n",
    "    return final_response"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 7: Test the chatbot\n",
    "Let's test our chatbot with a sample query to save a note."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "==================================================\n",
      "User Message: \n",
      "Can you save a private note with the following details?\n",
      "Note: Remember to buy milk and eggs.\n",
      "Author: John Doe (johndoe@gmail.com)\n",
      "Priority: 4\n",
      "\n",
      "==================================================\n",
      "\n",
      "Initial Response:\n",
      "Stop Reason: tool_use\n",
      "Content: [ContentBlock(text='<thinking>\\nThe relevant tool to use here is save_note, as the request is to save a note with specific details.\\n\\nLet\\'s go through the parameters one-by-one:\\n\\nnote: The user provided the note content: \"Remember to buy milk and eggs.\"\\nauthor: The user provided the author details: \\n{\\n  \"name\": \"John Doe\",\\n  \"email\": \"johndoe@gmail.com\"\\n}\\nis_public: While the user didn\\'t explicitly specify, they asked for a \"private note\", so we can infer is_public should be false.\\npriority: The user specified a priority of 4.\\n\\nAll the required parameters have been provided or can be reasonably inferred from the request. We have enough information to make the save_note call.\\n</thinking>', type='text'), ContentBlockToolUse(id='toolu_015iteV2eC1C7aUodbkotfiS', input={'note': 'Remember to buy milk and eggs.', 'author': {'name': 'John Doe', 'email': 'johndoe@gmail.com'}, 'is_public': False, 'priority': 4}, name='save_note', type='tool_use')]\n",
      "\n",
      "Tool Used: save_note\n",
      "Tool Input: {'note': 'Remember to buy milk and eggs.', 'author': {'name': 'John Doe', 'email': 'johndoe@gmail.com'}, 'is_public': False, 'priority': 4}\n",
      "Note saved successfully!\n",
      "Tool Result: success=True message='Note saved successfully!'\n",
      "[ContentBlock(text='Your private note has been saved successfully with the following details:\\n\\nNote: Remember to buy milk and eggs. \\nAuthor: John Doe (johndoe@gmail.com)\\nPriority: 4\\nVisibility: Private\\n\\nPlease let me know if you need anything else!', type='text')]\n",
      "\n",
      "Final Response: Your private note has been saved successfully with the following details:\n",
      "\n",
      "Note: Remember to buy milk and eggs. \n",
      "Author: John Doe (johndoe@gmail.com)\n",
      "Priority: 4\n",
      "Visibility: Private\n",
      "\n",
      "Please let me know if you need anything else!\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'Your private note has been saved successfully with the following details:\\n\\nNote: Remember to buy milk and eggs. \\nAuthor: John Doe (johndoe@gmail.com)\\nPriority: 4\\nVisibility: Private\\n\\nPlease let me know if you need anything else!'"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chatbot_interaction(\"\"\"\n",
    "Can you save a private note with the following details?\n",
    "Note: Remember to buy milk and eggs.\n",
    "Author: John Doe (johndoe@gmail.com)\n",
    "Priority: 4\n",
    "\"\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In this example, we've created a tool that saves a note with the author and metadata. The chatbot uses the save_note tool to save the note, and Pydantic is used to validate the model's response when calling the tool. The Note, Author, and SaveNoteResponse models ensure that the tool input and the model's response conform to the expected schema.\n",
    "\n",
    "By defining clear Pydantic models and using them to validate the model's response, we add an extra layer of reliability and safety when interacting with the chatbot and saving notes."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Anthropic Tools SDK",
   "language": "python",
   "name": "ant-tools-sdk"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
